Background

This notebook is for preparing input files for pyclone-vi (https://github.com/Roth-Lab/pyclone-vi). The same files will be used as an input for FastClone (https://github.com/GuanLab/FastClone_GuanLab).

Set up

suppressPackageStartupMessages({
  library(tidyverse)
  library(readxl)
  library(cDriver) # Calculate CCF, https://github.com/hanasusak/cDriver/
})

Directories and File Inputs/Outputs

# Detect the ".git" folder -- this will be in the project root directory
# Use this as the root directory to ensure proper sourcing of functions 
# no matter where this is called from
root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))
analysis_dir <- file.path(root_dir, "analyses", "tumor-clone-inference")
input_dir <- file.path(analysis_dir, "input")
data_dir <- file.path(root_dir, "data")
files_dir <- file.path(root_dir, "analyses", "sample-distribution-analysis", "results")
maf_files_dir <- file.path(root_dir, "analyses", "tmb-vaf-longitudinal", "results")

# Input files
#pbta_file <- file.path(files_dir, "pbta.tsv") # file from add-sample-distribution module
maf_file <- file.path(maf_files_dir, "tmb_vaf_genomic.tsv")
genomic_paired_file <- file.path(files_dir, "genomic_assays_matched_time_points.tsv") # file from add-sample-distribution module
nautilus_dec_file <- file.path(input_dir, "deceased_samples.xlsx") 
palette_file <- file.path(root_dir, "figures", "palettes", "tumor_descriptor_color_palette.tsv")


# EXAMPLE 
# But to replace with dir with cns data inferred by CNVkit
# PT_Z4BF2NSB_dir <- file.path(input_dir, "cnvkit_data_example/PT_Z4BF2NSB") 
cns_dir <- file.path(input_dir, "cnvkit_data") 

# File path to results directory
results_dir <-
  file.path(analysis_dir, "results")
if (!dir.exists(results_dir)) {
  dir.create(results_dir)
}

# File path to input directory
pyclonevi_input_dir <-
  file.path(analysis_dir, "results", "pyclone-vi-input")
if (!dir.exists(pyclonevi_input_dir)) {
  dir.create(pyclonevi_input_dir)
}

# File path to input directory
fastclone_input_dir <-
  file.path(analysis_dir, "results", "fastclone-input")
if (!dir.exists(pyclonevi_input_dir)) {
  dir.create(pyclonevi_input_dir)
}


# File path to plot directory
pyclone_plots_dir <-
  file.path(analysis_dir, "plots", "pyclone-vi")
if (!dir.exists(plots_dir)) {
  dir.create(plots_dir)
}


source(paste0(root_dir, "/figures/scripts/theme.R"))

Load and process data

# Read maf
maf_df <- readr::read_tsv(maf_file, guess_max = 100000, show_col_types = FALSE) %>%
  select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, cg_id, tumor_descriptor,
         sample_id, aliquot_id, Chromosome, 
         Start_Position, Reference_Allele, Tumor_Seq_Allele1, Tumor_Seq_Allele2,
         t_ref_count, t_alt_count, tmb, 
         tumor_fraction, tumor_ploidy, VAF, Hugo_Symbol) %>% 

  # Remove hypermutants
  filter(!tmb >= 10, 
  
  # There are alterations with high number of read counts.
  # We will exclude those with >1000 for now.
         !t_alt_count >= 1000,
         !t_ref_count >= 1000)  %>% 
  
  # Add `normal_cn`: Total copy number of segment in healthy tissue. 
  # For autosome this will be two and male sex chromosomes one.
  # See: https://github.com/Roth-Lab/pyclone-vi
  mutate(normal_cn = case_when(grepl("chrY", Chromosome) ~ "1", 
                                     TRUE ~ "2"))
# Calculate CCF
maf_df <- CCF(maf_df)
 
# Read patient list
genomic_paired_df <- readr::read_tsv(genomic_paired_file, guess_max = 100000, show_col_types = FALSE) %>% 
  select(!c(cancer_group, experimental_strategy)) %>% 
  left_join(maf_df, by = c("Kids_First_Participant_ID")) %>% # create unique identifiers
  dplyr::mutate(mutation_id = paste(Kids_First_Participant_ID, tumor_descriptor, Kids_First_Biospecimen_ID, Chromosome, Start_Position, Reference_Allele, Tumor_Seq_Allele2, sep = ":"),
                cg_id_kids = paste(cg_id, Kids_First_Participant_ID, sep = "_"),
                cg_id_kids = str_replace(cg_id_kids, "/|-", "_"),
                cg_id_kids = str_replace_all(cg_id_kids, " ", "_"),
                cg_id = str_replace(cg_id, "/|-", "_"),
                cg_id = str_replace_all(cg_id, " ", "_"),
                sample_id = paste(tumor_descriptor, Kids_First_Biospecimen_ID, sep = ":"))


# Let's count #specimens per sample 
# We will remove any samples with less than 2 specimens as phylogenies require at least 3 taxa
kids_specimens_n_df <- genomic_paired_df %>% 
  select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, tumor_descriptor) %>% 
  unique() %>% 
  dplyr::count(Kids_First_Participant_ID) %>% 
  dplyr::mutate(kids_specimens_n = glue::glue("{Kids_First_Participant_ID}  (N={n})")) %>%
  dplyr::rename(kids_specimens_number = n) %>% 
  filter(!kids_specimens_number <= 2) %>%

  left_join(genomic_paired_df, by = c("Kids_First_Participant_ID")) %>%
  #left_join(maf_df) %>% 
  filter(!is.na(Chromosome)) 
  
# Let's confirm and add the number of timepoints per sample
# In case we lost any from filtering hypermutants and high reads/alteration
timepoints_n_df <- kids_specimens_n_df %>% 
  select(Kids_First_Participant_ID, tumor_descriptor) %>% 
  unique() %>% 
  dplyr::count(Kids_First_Participant_ID) %>% 
  dplyr::mutate(kids_timepoints_n = glue::glue("{Kids_First_Participant_ID}  (N={n})")) %>%
  dplyr::rename(kids_timepoints_number = n) %>% 
  filter(!kids_timepoints_number <= 1)

df <- timepoints_n_df %>% 
  left_join(kids_specimens_n_df, by = c("Kids_First_Participant_ID")) %>% 
  write_tsv(file.path(results_dir, "samples_eligible_for_phylogeny.tsv"))

# List with samples eligible for phylogeny
# I added the information about to use for phylogenetic inferences
list_df <- df %>% 
  select(Kids_First_Participant_ID) %>% 
  unique() %>% 
  mutate(somatic_germline_phylogeny = case_when(grepl("PT_KZ56XHJT|PT_KTRJ8TFY", Kids_First_Participant_ID) ~ "yes",
                                        TRUE ~ "not_yet")) %>% 
  write_tsv(file.path(results_dir, "samples_eligible_for_phylogeny_list.tsv"))

Add Nautilus location for Deceased specimens

nautilus_dec_df <- read_excel(nautilus_dec_file) %>% 
  right_join(df, by = c("sample_id", "aliquot_id", "tumor_descriptor")) %>% 
  write_tsv(file.path(results_dir, "nautilus_dec.tsv"))

list_df <- nautilus_dec_df %>% 
  select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, `Note field from Nautilus of initial parent`) %>% 
  unique() %>% 
  write_tsv(file.path(results_dir, "nautilus_dec_list.tsv"))

Other, maybe to delete


# Make list with samples with >2 biospecimens at Deceased timepoint
dec_n_df <- df %>% 
  filter(tumor_descriptor == "Deceased") %>% 
  select(Kids_First_Participant_ID, tumor_descriptor, Kids_First_Biospecimen_ID) %>% 
  unique() %>% 
  dplyr::count(Kids_First_Participant_ID) %>% 
 # dplyr::mutate(kids_dec_n = glue::glue("{Kids_First_Participant_ID}  (N={n})")) %>%
  dplyr::rename(kids_deceased_bs_number = n) %>% 
  filter(!kids_deceased_bs_number <= 1) %>% 
  write_tsv(file.path(results_dir, "kids_dec_multiple_bs_list.tsv"))

###-----------------------------------------------------
# let's look into PT_3KM9W8S8
PT_3KM9W8S8_df <- nautilus_dec_df %>% 
  filter(Kids_First_Participant_ID == "PT_3KM9W8S8",
         Kids_First_Biospecimen_ID == "BS_2NQXY528") %>% 
  select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, `Note field from Nautilus of initial parent`) %>% 
  unique() %>% 
  write_tsv(file.path(results_dir, "nautilus_dec_list.tsv"))
  
#bs_id <- print(unique(PT_3KM9W8S8_df$Kids_First_Biospecimen_ID))

Create pyclone input files

We need to generate the input files according to the method’s template. Phylogenetic methods require at least 2 samples per tumor site (multiregional sampling per anatomical site). Here, we will consider kids samples with more than 2 timepoints with one or more biospecimens. We will compare later differences in samples with single vs multiple biospecimens.

# Create pyclone df for all samples
pyclone_all_samples_df <- nautilus_dec_df %>%
  select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, cg_id, 
         cg_id_kids, mutation_id, sample_id,
         tumor_descriptor, Chromosome, Start_Position, 
         Reference_Allele, Tumor_Seq_Allele2, t_ref_count, t_alt_count,
         normal_cn, tumor_fraction) %>% 
  
  # change names to match input requirements
  dplyr::rename("ref_counts" = "t_ref_count", 
                "alt_counts" = "t_alt_count",
                "tumour_content" = "tumor_fraction") %>%
  select(Kids_First_Participant_ID, Kids_First_Biospecimen_ID, tumor_descriptor, 
         cg_id_kids, mutation_id, sample_id, ref_counts, 
         alt_counts, normal_cn, tumour_content)
 

# I will test one HGG dataset for now
# PT_Z4BF2NSB

#PT_Z4BF2NSB_DF <- pyclone_all_samples_df %>% 
#  filter(Kids_First_Participant_ID == "PT_Z4BF2NSB")
                   

Add major_cn, minor_cn columns from cns files

data_dir <- dir(path = cns_dir,  pattern = ".call.cns", full.names = TRUE, recursive = TRUE)

# Create list 
data_list <- list()

for (i in 1:length(data_dir) ) { 
  
  # Create sample_name
  sample_name <-  unique(as.character(gsub(".call.cns", "", str_split_fixed(data_dir[i], "/", 13)[,11])))
  sample_name <- sort(sample_name, decreasing = FALSE)
  print(sample_name)
  
  for (x in seq_along(sample_name) ) { 
    
    data_list[[i]] <- read.csv(data_dir[i], header=T, sep="\t") 
  
    
  # Create file_name
  file_name <- gsub(".call.cns", "", str_split_fixed(data_dir[i], "/", 13)[,12])
  print(file_name)
  
  data_list[[i]] <- data_list[[i]] %>% 
    mutate(Kids_First_Biospecimen_ID = file_name)
  
  # The following code assigns name to df
  # But adds an extra df into the list - dont know yet why but let's remove, it's not necessary
  #df <- assign(file_name, data_list) 
  #data_list[[file_name]] <- df
  }
}
[1] "PT_KTRJ8TFY"
[1] "BS_3VKW5988"
[1] "PT_KTRJ8TFY"
[1] "BS_402W79TS"
[1] "PT_KTRJ8TFY"
[1] "BS_5968GBGT"
[1] "PT_KTRJ8TFY"
[1] "BS_AF5D41PD"
[1] "PT_KTRJ8TFY"
[1] "BS_BQ81D2BP"
[1] "PT_KTRJ8TFY"
[1] "BS_EE73VE7V"
[1] "PT_KTRJ8TFY"
[1] "BS_HYKV2TH9"
[1] "PT_KTRJ8TFY"
[1] "BS_NGSG2KB6"
[1] "PT_KZ56XHJT"
[1] "BS_0ATJ22QA"
[1] "PT_KZ56XHJT"
[1] "BS_1Q524P3B"
[1] "PT_KZ56XHJT"
[1] "BS_21ET39G7"
[1] "PT_KZ56XHJT"
[1] "BS_22VCR7DF"
[1] "PT_KZ56XHJT"
[1] "BS_9DN4QR6E"
[1] "PT_KZ56XHJT"
[1] "BS_AK9BV52G"
[1] "PT_KZ56XHJT"
[1] "BS_D6STCMQS"
[1] "PT_KZ56XHJT"
[1] "BS_FCDAH728"
[1] "PT_KZ56XHJT"
[1] "BS_FWP8ZA4K"
[1] "PT_KZ56XHJT"
[1] "BS_H8NWA41N"
[1] "PT_KZ56XHJT"
[1] "BS_X5VN0FW0"
[1] "PT_KZ56XHJT"
[1] "BS_YHXMYDBN"
[1] "PT_Z4BF2NSB"
[1] "BS_2T4EJ6KN"
[1] "PT_Z4BF2NSB"
[1] "BS_537YFJ06"
[1] "PT_Z4BF2NSB"
[1] "BS_C6T8F9K7"
[1] "PT_Z4BF2NSB"
[1] "BS_EJP43CD9"
[1] "PT_Z4BF2NSB"
[1] "BS_GSXNQNRY"
[1] "PT_Z4BF2NSB"
[1] "BS_M29BNE7Z"
[1] "PT_Z4BF2NSB"
[1] "BS_MRN9VDQ0"
[1] "PT_Z4BF2NSB"
[1] "BS_W2QCHQ7E"
# Bind all df from list 
data_list_bind <- dplyr::bind_rows(data_list) 

# Create and save pyclone_input!
pyclone_input <- data_list_bind %>% 
  
  # Rename to match input format
  # To figure out if the assignment is correct
  dplyr::rename("major_cn" = "cn1", 
                "minor_cn" = "cn2") %>% 

  left_join(pyclone_all_samples_df) %>% 
  filter(!is.na(major_cn),
         !is.na(minor_cn)) %>%
  dplyr::mutate(mutation_id = paste(mutation_id, major_cn, minor_cn,sep = ":")) %>% 
  select(Kids_First_Participant_ID, tumor_descriptor, cg_id_kids, mutation_id, sample_id, ref_counts, 
         alt_counts, normal_cn, major_cn, minor_cn, tumour_content) %>% 
  
  # To ensure there are no duplicated entries in the dataframe
  distinct() 
Joining with `by = join_by(Kids_First_Biospecimen_ID)`Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
for (i in 1:length(data_dir) ) { 
  
  # Create sample_name
  sample_name <-  unique(as.character(gsub(".call.cns", "", str_split_fixed(data_dir[i], "/", 13)[,11])))
  sample_name <- sort(sample_name, decreasing = FALSE)
  print(sample_name)
  
for (x in seq_along(sample_name) ) { 
  
  # Save input file for pyclone-vi
  fname <- paste0(pyclonevi_input_dir, "/", sample_name[x], ".tsv")
  print(fname)
  
  pyclone_input_subset <- pyclone_input %>%
    filter(Kids_First_Participant_ID == sample_name[x]) %>% 
    #select(-c(Kids_First_Participant_ID)) %>% 
  write_tsv(file.path(fname))
  
  
  # Save input file for FastClone
  fastclone_fname <- paste0(fastclone_input_dir, "/", sample_name[x], ".tsv")
  print(fastclone_fname)
  
  fastclone_input_subset <- pyclone_input %>%
    dplyr::rename("var_counts" = "alt_counts") %>% 
    filter(Kids_First_Participant_ID == sample_name[x]) %>% 
    #select(-c(Kids_First_Participant_ID)) %>% 
  write_tsv(file.path(fastclone_fname))
    
    
    
}
}
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KTRJ8TFY"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KTRJ8TFY.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KTRJ8TFY.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_KZ56XHJT"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_KZ56XHJT.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_KZ56XHJT.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
[1] "PT_Z4BF2NSB"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/pyclone-vi-input/PT_Z4BF2NSB.tsv"
[1] "/Users/chronia/CHOP/GitHub/pbta-tumor-evolution/analyses/tumor-clone-inference/results/fastclone-input/PT_Z4BF2NSB.tsv"
# To find the position of duplicate elements in x, use this:
# duplicated(pyclone_input)

# Extract duplicate elements:
# pyclone_input[duplicated(pyclone_input)]

Plot depth coverage

# Read color palette
palette_df <- readr::read_tsv(palette_file, guess_max = 100000, show_col_types = FALSE) %>% 
  mutate(tumor_descriptor = color_names)

# Define and order palette
palette <- palette_df$hex_codes
names(palette) <- palette_df$tumor_descriptor

# Define timepoints
timepoints = c("Diagnosis", "Progressive", "Recurrence", "Deceased", "Second Malignancy", "Unavailable")
for (i in 1:length(data_dir) ) { 
  
  # Create sample_name
  sample_name <-  unique(as.character(gsub(".call.cns", "", str_split_fixed(data_dir[i], "/", 13)[,11])))
  sample_name <- sort(sample_name, decreasing = FALSE)
  print(sample_name)
  
for (x in seq_along(sample_name) ) { 
  
  pyclone_input_subset <- pyclone_input %>%
    filter(Kids_First_Participant_ID == sample_name[x]) %>% 
    select(-c(Kids_First_Participant_ID)) #%>% 
   # mutate(tumor_descriptor = factor(tumor_descriptor),
    #     tumor_descriptor = fct_relevel(tumor_descriptor, timepoints)) %>% 
    #arrange(tumor_descriptor, sample_id)
  
  # Make this reproducible
  set.seed(2023)

  # Define label for plots
  Timepoint <- factor(x = pyclone_input_subset$tumor_descriptor, levels = timepoints)
  
  #######################
  # Create bxp ref_counts
    p <- print(ggplot(pyclone_input_subset, aes(sample_id, ref_counts, color = Timepoint)) + 
                 geom_jitter(width = 0.15, size = 0.7, alpha = 0.6) +
                 ggplot2::geom_boxplot(color = "black",
                              size = 0.25,
                              alpha = 0,
                              coef = 0) + # remove whiskers
                 theme_Publication() + 
                 scale_color_manual(values = palette,
                                    breaks = sort(names(palette))) +
                 #rotate() +
                 theme(axis.text.x = element_text(angle = 90)) +
                 stat_summary(fun.y=mean,shape=1,col='black',geom='point') +
                 labs(title = sample_name[x],
                      x = "sample_id",
                      y = "ref_counts",
                      color = "Timepoint"))
    
    # Save the plot
    ggsave(filename = paste0(sample_name[x], "-ref_counts.pdf"), 
         path = pyclone_plots_dir, 
         width = 6, 
         height = 5, 
         device = "pdf", 
        useDingbats = FALSE)
    

  #######################
  # Create bxp alt_counts
    p <- print(ggplot(pyclone_input_subset, aes(sample_id, alt_counts, color = Timepoint)) + 
                 geom_jitter(width = 0.15, size = 0.7, alpha = 0.6) +
                 ggplot2::geom_boxplot(color = "black",
                              size = 0.25,
                              alpha = 0,
                              coef = 0) + # remove whiskers
                 theme_Publication() + 
                 scale_color_manual(values = palette,
                                    breaks = sort(names(palette))) +
                 #rotate() +
                 theme(axis.text.x = element_text(angle = 90)) +
                 stat_summary(fun.y=mean,shape=1,col='black',geom='point') +
                 labs(title = sample_name[x],
                      x = "sample_id",
                      y = "alt_counts",
                      color = "Timepoint"))
    
    # Save the plot
    ggsave(filename = paste0(sample_name[x], "-alt_counts.pdf"), 
         path = pyclone_plots_dir, 
         width = 6, 
         height = 5, 
         device = "pdf", 
        useDingbats = FALSE)
  
  
}
}
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KTRJ8TFY"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_KZ56XHJT"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"
[1] "PT_Z4BF2NSB"

sessionInfo()
R version 4.3.1 (2023-06-16)
Platform: x86_64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.6.1

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-x86_64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: America/New_York
tzcode source: internal

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] ggthemes_4.2.4   cDriver_0.4.2    readxl_1.4.3     gridExtra_2.3    clonevol_0.99.11 lubridate_1.9.3  forcats_1.0.0    stringr_1.5.1    dplyr_1.1.4     
[10] purrr_1.0.2      readr_2.1.4      tidyr_1.3.0      tibble_3.2.1     ggplot2_3.4.4    tidyverse_2.0.0  dndscv_0.0.1.0  

loaded via a namespace (and not attached):
 [1] ade4_1.7-22             tidyselect_1.2.0        Rmpfr_0.9-3             farver_2.1.1            Biostrings_2.70.1       bitops_1.0-7           
 [7] fastmap_1.1.1           RCurl_1.98-1.13         digest_0.6.33           timechange_0.2.0        lifecycle_1.0.4         magrittr_2.0.3         
[13] compiler_4.3.1          rlang_1.1.2             sass_0.4.7              tools_4.3.1             utf8_1.2.4              yaml_2.3.7             
[19] data.table_1.14.8       knitr_1.45              labeling_0.4.3          bit_4.0.5               abind_1.4-5             BiocParallel_1.36.0    
[25] withr_2.5.2             BiocGenerics_0.48.1     stats4_4.3.1            fansi_1.0.5             colorspace_2.1-0        scales_1.2.1           
[31] MASS_7.3-60             cli_3.6.1               rmarkdown_2.25          crayon_1.5.2            ragg_1.2.6              generics_0.1.3         
[37] rstudioapi_0.15.0       tzdb_0.4.0              cachem_1.0.8            zlibbioc_1.48.0         parallel_4.3.1          cellranger_1.1.0       
[43] XVector_0.42.0          vctrs_0.6.4             jsonlite_1.8.7          carData_3.0-5           car_3.1-2               IRanges_2.36.0         
[49] hms_1.1.3               S4Vectors_0.40.1        bit64_4.0.5             rstatix_0.7.2           seqinr_4.2-30           systemfonts_1.0.5      
[55] jquerylib_0.1.4         glue_1.6.2              codetools_0.2-19        stringi_1.8.1           gtable_0.3.4            GenomeInfoDb_1.38.1    
[61] GenomicRanges_1.54.1    gmp_0.7-2               munsell_0.5.0           pillar_1.9.0            htmltools_0.5.7         GenomeInfoDbData_1.2.11
[67] R6_2.5.1                textshaping_0.3.7       rprojroot_2.0.4         vroom_1.6.4             evaluate_0.23           backports_1.4.1        
[73] Rsamtools_2.18.0        broom_1.0.5             bslib_0.5.1             Rcpp_1.0.11             xfun_0.41               pkgconfig_2.0.3        
LS0tCnRpdGxlOiAiSW5mZXJlbmNlIG9mIHN1YmNsb25hbCBhcmNoaXRlY3R1cmUgb2YgdHVtb3JzIGFjcm9zcyBtdWx0aXBsZSB0aW1lcG9pbnRzIGluIHRoZSBwYWlyZWQgbG9uZ2l0dWRpbmFsIChQTCkgY29ob3J0IgphdXRob3I6ICdBbnRvbmlhIENocm9uaSA8Y2hyb25pYUBjaG9wLmVkdT4gZm9yIEQzQicKZGF0ZTogIjIwMjMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKLS0tCgojIEJhY2tncm91bmQKClRoaXMgbm90ZWJvb2sgaXMgZm9yIHByZXBhcmluZyBpbnB1dCBmaWxlcyBmb3IgcHljbG9uZS12aSAoaHR0cHM6Ly9naXRodWIuY29tL1JvdGgtTGFiL3B5Y2xvbmUtdmkpLiBUaGUgc2FtZSBmaWxlcyB3aWxsIGJlIHVzZWQgYXMgYW4gaW5wdXQgZm9yIEZhc3RDbG9uZSAoaHR0cHM6Ly9naXRodWIuY29tL0d1YW5MYWIvRmFzdENsb25lX0d1YW5MYWIpLgoKCiMgU2V0IHVwCgpgYGB7ciBsb2FkLWxpYnJhcnl9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeSh0aWR5dmVyc2UpCiAgbGlicmFyeShyZWFkeGwpCiAgbGlicmFyeShjRHJpdmVyKSAjIENhbGN1bGF0ZSBDQ0YsIGh0dHBzOi8vZ2l0aHViLmNvbS9oYW5hc3VzYWsvY0RyaXZlci8KfSkKYGBgCgojIERpcmVjdG9yaWVzIGFuZCBGaWxlIElucHV0cy9PdXRwdXRzCgpgYGB7ciBzZXQtZGlyLWFuZC1maWxlLW5hbWVzfQojIERldGVjdCB0aGUgIi5naXQiIGZvbGRlciAtLSB0aGlzIHdpbGwgYmUgaW4gdGhlIHByb2plY3Qgcm9vdCBkaXJlY3RvcnkKIyBVc2UgdGhpcyBhcyB0aGUgcm9vdCBkaXJlY3RvcnkgdG8gZW5zdXJlIHByb3BlciBzb3VyY2luZyBvZiBmdW5jdGlvbnMgCiMgbm8gbWF0dGVyIHdoZXJlIHRoaXMgaXMgY2FsbGVkIGZyb20Kcm9vdF9kaXIgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290OjpoYXNfZGlyKCIuZ2l0IikpCmFuYWx5c2lzX2RpciA8LSBmaWxlLnBhdGgocm9vdF9kaXIsICJhbmFseXNlcyIsICJ0dW1vci1jbG9uZS1pbmZlcmVuY2UiKQppbnB1dF9kaXIgPC0gZmlsZS5wYXRoKGFuYWx5c2lzX2RpciwgImlucHV0IikKZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHJvb3RfZGlyLCAiZGF0YSIpCmZpbGVzX2RpciA8LSBmaWxlLnBhdGgocm9vdF9kaXIsICJhbmFseXNlcyIsICJzYW1wbGUtZGlzdHJpYnV0aW9uLWFuYWx5c2lzIiwgInJlc3VsdHMiKQptYWZfZmlsZXNfZGlyIDwtIGZpbGUucGF0aChyb290X2RpciwgImFuYWx5c2VzIiwgInRtYi12YWYtbG9uZ2l0dWRpbmFsIiwgInJlc3VsdHMiKQoKIyBJbnB1dCBmaWxlcwojcGJ0YV9maWxlIDwtIGZpbGUucGF0aChmaWxlc19kaXIsICJwYnRhLnRzdiIpICMgZmlsZSBmcm9tIGFkZC1zYW1wbGUtZGlzdHJpYnV0aW9uIG1vZHVsZQptYWZfZmlsZSA8LSBmaWxlLnBhdGgobWFmX2ZpbGVzX2RpciwgInRtYl92YWZfZ2Vub21pYy50c3YiKQpnZW5vbWljX3BhaXJlZF9maWxlIDwtIGZpbGUucGF0aChmaWxlc19kaXIsICJnZW5vbWljX2Fzc2F5c19tYXRjaGVkX3RpbWVfcG9pbnRzLnRzdiIpICMgZmlsZSBmcm9tIGFkZC1zYW1wbGUtZGlzdHJpYnV0aW9uIG1vZHVsZQpuYXV0aWx1c19kZWNfZmlsZSA8LSBmaWxlLnBhdGgoaW5wdXRfZGlyLCAiZGVjZWFzZWRfc2FtcGxlcy54bHN4IikgCnBhbGV0dGVfZmlsZSA8LSBmaWxlLnBhdGgocm9vdF9kaXIsICJmaWd1cmVzIiwgInBhbGV0dGVzIiwgInR1bW9yX2Rlc2NyaXB0b3JfY29sb3JfcGFsZXR0ZS50c3YiKQoKCiMgRVhBTVBMRSAKIyBCdXQgdG8gcmVwbGFjZSB3aXRoIGRpciB3aXRoIGNucyBkYXRhIGluZmVycmVkIGJ5IENOVmtpdAojIFBUX1o0QkYyTlNCX2RpciA8LSBmaWxlLnBhdGgoaW5wdXRfZGlyLCAiY252a2l0X2RhdGFfZXhhbXBsZS9QVF9aNEJGMk5TQiIpIApjbnNfZGlyIDwtIGZpbGUucGF0aChpbnB1dF9kaXIsICJjbnZraXRfZGF0YSIpIAoKIyBGaWxlIHBhdGggdG8gcmVzdWx0cyBkaXJlY3RvcnkKcmVzdWx0c19kaXIgPC0KICBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAicmVzdWx0cyIpCmlmICghZGlyLmV4aXN0cyhyZXN1bHRzX2RpcikpIHsKICBkaXIuY3JlYXRlKHJlc3VsdHNfZGlyKQp9CgojIEZpbGUgcGF0aCB0byBpbnB1dCBkaXJlY3RvcnkKcHljbG9uZXZpX2lucHV0X2RpciA8LQogIGZpbGUucGF0aChhbmFseXNpc19kaXIsICJyZXN1bHRzIiwgInB5Y2xvbmUtdmktaW5wdXQiKQppZiAoIWRpci5leGlzdHMocHljbG9uZXZpX2lucHV0X2RpcikpIHsKICBkaXIuY3JlYXRlKHB5Y2xvbmV2aV9pbnB1dF9kaXIpCn0KCiMgRmlsZSBwYXRoIHRvIGlucHV0IGRpcmVjdG9yeQpmYXN0Y2xvbmVfaW5wdXRfZGlyIDwtCiAgZmlsZS5wYXRoKGFuYWx5c2lzX2RpciwgInJlc3VsdHMiLCAiZmFzdGNsb25lLWlucHV0IikKaWYgKCFkaXIuZXhpc3RzKHB5Y2xvbmV2aV9pbnB1dF9kaXIpKSB7CiAgZGlyLmNyZWF0ZShweWNsb25ldmlfaW5wdXRfZGlyKQp9CgoKIyBGaWxlIHBhdGggdG8gcGxvdCBkaXJlY3RvcnkKcHljbG9uZV9wbG90c19kaXIgPC0KICBmaWxlLnBhdGgoYW5hbHlzaXNfZGlyLCAicGxvdHMiLCAicHljbG9uZS12aSIpCmlmICghZGlyLmV4aXN0cyhwbG90c19kaXIpKSB7CiAgZGlyLmNyZWF0ZShwbG90c19kaXIpCn0KCgpzb3VyY2UocGFzdGUwKHJvb3RfZGlyLCAiL2ZpZ3VyZXMvc2NyaXB0cy90aGVtZS5SIikpCmBgYAoKIyBMb2FkIGFuZCBwcm9jZXNzIGRhdGEKCmBgYHtyIGxvYWQtcHJvY2Vzcy1pbnB1dHN9CiMgUmVhZCBtYWYKbWFmX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihtYWZfZmlsZSwgZ3Vlc3NfbWF4ID0gMTAwMDAwLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKSAlPiUKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgY2dfaWQsIHR1bW9yX2Rlc2NyaXB0b3IsCiAgICAgICAgIHNhbXBsZV9pZCwgYWxpcXVvdF9pZCwgQ2hyb21vc29tZSwgCiAgICAgICAgIFN0YXJ0X1Bvc2l0aW9uLCBSZWZlcmVuY2VfQWxsZWxlLCBUdW1vcl9TZXFfQWxsZWxlMSwgVHVtb3JfU2VxX0FsbGVsZTIsCiAgICAgICAgIHRfcmVmX2NvdW50LCB0X2FsdF9jb3VudCwgdG1iLCAKICAgICAgICAgdHVtb3JfZnJhY3Rpb24sIHR1bW9yX3Bsb2lkeSwgVkFGLCBIdWdvX1N5bWJvbCkgJT4lIAoKICAjIFJlbW92ZSBoeXBlcm11dGFudHMKICBmaWx0ZXIoIXRtYiA+PSAxMCwgCiAgCiAgIyBUaGVyZSBhcmUgYWx0ZXJhdGlvbnMgd2l0aCBoaWdoIG51bWJlciBvZiByZWFkIGNvdW50cy4KICAjIFdlIHdpbGwgZXhjbHVkZSB0aG9zZSB3aXRoID4xMDAwIGZvciBub3cuCiAgICAgICAgICF0X2FsdF9jb3VudCA+PSAxMDAwLAogICAgICAgICAhdF9yZWZfY291bnQgPj0gMTAwMCkgICU+JSAKICAKICAjIEFkZCBgbm9ybWFsX2NuYDogVG90YWwgY29weSBudW1iZXIgb2Ygc2VnbWVudCBpbiBoZWFsdGh5IHRpc3N1ZS4gCiAgIyBGb3IgYXV0b3NvbWUgdGhpcyB3aWxsIGJlIHR3byBhbmQgbWFsZSBzZXggY2hyb21vc29tZXMgb25lLgogICMgU2VlOiBodHRwczovL2dpdGh1Yi5jb20vUm90aC1MYWIvcHljbG9uZS12aQogIG11dGF0ZShub3JtYWxfY24gPSBjYXNlX3doZW4oZ3JlcGwoImNoclkiLCBDaHJvbW9zb21lKSB+ICIxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIjIiKSkKIyBDYWxjdWxhdGUgQ0NGCm1hZl9kZiA8LSBDQ0YobWFmX2RmKQogCiMgUmVhZCBwYXRpZW50IGxpc3QKZ2Vub21pY19wYWlyZWRfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KGdlbm9taWNfcGFpcmVkX2ZpbGUsIGd1ZXNzX21heCA9IDEwMDAwMCwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkgJT4lIAogIHNlbGVjdCghYyhjYW5jZXJfZ3JvdXAsIGV4cGVyaW1lbnRhbF9zdHJhdGVneSkpICU+JSAKICBsZWZ0X2pvaW4obWFmX2RmLCBieSA9IGMoIktpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQiKSkgJT4lICMgY3JlYXRlIHVuaXF1ZSBpZGVudGlmaWVycwogIGRwbHlyOjptdXRhdGUobXV0YXRpb25faWQgPSBwYXN0ZShLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCB0dW1vcl9kZXNjcmlwdG9yLCBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCBDaHJvbW9zb21lLCBTdGFydF9Qb3NpdGlvbiwgUmVmZXJlbmNlX0FsbGVsZSwgVHVtb3JfU2VxX0FsbGVsZTIsIHNlcCA9ICI6IiksCiAgICAgICAgICAgICAgICBjZ19pZF9raWRzID0gcGFzdGUoY2dfaWQsIEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIHNlcCA9ICJfIiksCiAgICAgICAgICAgICAgICBjZ19pZF9raWRzID0gc3RyX3JlcGxhY2UoY2dfaWRfa2lkcywgIi98LSIsICJfIiksCiAgICAgICAgICAgICAgICBjZ19pZF9raWRzID0gc3RyX3JlcGxhY2VfYWxsKGNnX2lkX2tpZHMsICIgIiwgIl8iKSwKICAgICAgICAgICAgICAgIGNnX2lkID0gc3RyX3JlcGxhY2UoY2dfaWQsICIvfC0iLCAiXyIpLAogICAgICAgICAgICAgICAgY2dfaWQgPSBzdHJfcmVwbGFjZV9hbGwoY2dfaWQsICIgIiwgIl8iKSwKICAgICAgICAgICAgICAgIHNhbXBsZV9pZCA9IHBhc3RlKHR1bW9yX2Rlc2NyaXB0b3IsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIHNlcCA9ICI6IikpCgoKIyBMZXQncyBjb3VudCAjc3BlY2ltZW5zIHBlciBzYW1wbGUgCiMgV2Ugd2lsbCByZW1vdmUgYW55IHNhbXBsZXMgd2l0aCBsZXNzIHRoYW4gMiBzcGVjaW1lbnMgYXMgcGh5bG9nZW5pZXMgcmVxdWlyZSBhdCBsZWFzdCAzIHRheGEKa2lkc19zcGVjaW1lbnNfbl9kZiA8LSBnZW5vbWljX3BhaXJlZF9kZiAlPiUgCiAgc2VsZWN0KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIHR1bW9yX2Rlc2NyaXB0b3IpICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgZHBseXI6OmNvdW50KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpICU+JSAKICBkcGx5cjo6bXV0YXRlKGtpZHNfc3BlY2ltZW5zX24gPSBnbHVlOjpnbHVlKCJ7S2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRH0gIChOPXtufSkiKSkgJT4lCiAgZHBseXI6OnJlbmFtZShraWRzX3NwZWNpbWVuc19udW1iZXIgPSBuKSAlPiUgCiAgZmlsdGVyKCFraWRzX3NwZWNpbWVuc19udW1iZXIgPD0gMikgJT4lCgogIGxlZnRfam9pbihnZW5vbWljX3BhaXJlZF9kZiwgYnkgPSBjKCJLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEIikpICU+JQogICNsZWZ0X2pvaW4obWFmX2RmKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShDaHJvbW9zb21lKSkgCiAgCiMgTGV0J3MgY29uZmlybSBhbmQgYWRkIHRoZSBudW1iZXIgb2YgdGltZXBvaW50cyBwZXIgc2FtcGxlCiMgSW4gY2FzZSB3ZSBsb3N0IGFueSBmcm9tIGZpbHRlcmluZyBoeXBlcm11dGFudHMgYW5kIGhpZ2ggcmVhZHMvYWx0ZXJhdGlvbgp0aW1lcG9pbnRzX25fZGYgPC0ga2lkc19zcGVjaW1lbnNfbl9kZiAlPiUgCiAgc2VsZWN0KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIHR1bW9yX2Rlc2NyaXB0b3IpICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgZHBseXI6OmNvdW50KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpICU+JSAKICBkcGx5cjo6bXV0YXRlKGtpZHNfdGltZXBvaW50c19uID0gZ2x1ZTo6Z2x1ZSgie0tpZHNfRmlyc3RfUGFydGljaXBhbnRfSUR9ICAoTj17bn0pIikpICU+JQogIGRwbHlyOjpyZW5hbWUoa2lkc190aW1lcG9pbnRzX251bWJlciA9IG4pICU+JSAKICBmaWx0ZXIoIWtpZHNfdGltZXBvaW50c19udW1iZXIgPD0gMSkKCmRmIDwtIHRpbWVwb2ludHNfbl9kZiAlPiUgCiAgbGVmdF9qb2luKGtpZHNfc3BlY2ltZW5zX25fZGYsIGJ5ID0gYygiS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCIpKSAlPiUgCiAgd3JpdGVfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgInNhbXBsZXNfZWxpZ2libGVfZm9yX3BoeWxvZ2VueS50c3YiKSkKCiMgTGlzdCB3aXRoIHNhbXBsZXMgZWxpZ2libGUgZm9yIHBoeWxvZ2VueQojIEkgYWRkZWQgdGhlIGluZm9ybWF0aW9uIGFib3V0IHRvIHVzZSBmb3IgcGh5bG9nZW5ldGljIGluZmVyZW5jZXMKbGlzdF9kZiA8LSBkZiAlPiUgCiAgc2VsZWN0KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpICU+JSAKICB1bmlxdWUoKSAlPiUgCiAgbXV0YXRlKHNvbWF0aWNfZ2VybWxpbmVfcGh5bG9nZW55ID0gY2FzZV93aGVuKGdyZXBsKCJQVF9LWjU2WEhKVHxQVF9LVFJKOFRGWSIsIEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpIH4gInllcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIm5vdF95ZXQiKSkgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJzYW1wbGVzX2VsaWdpYmxlX2Zvcl9waHlsb2dlbnlfbGlzdC50c3YiKSkKCmBgYAoKIyMgQWRkIE5hdXRpbHVzIGxvY2F0aW9uIGZvciBEZWNlYXNlZCBzcGVjaW1lbnMKCmBgYHtyIGFkZC1OYXV0aWx1cy1sb2NhdGlvbn0KbmF1dGlsdXNfZGVjX2RmIDwtIHJlYWRfZXhjZWwobmF1dGlsdXNfZGVjX2ZpbGUpICU+JSAKICByaWdodF9qb2luKGRmLCBieSA9IGMoInNhbXBsZV9pZCIsICJhbGlxdW90X2lkIiwgInR1bW9yX2Rlc2NyaXB0b3IiKSkgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJuYXV0aWx1c19kZWMudHN2IikpCgpsaXN0X2RmIDwtIG5hdXRpbHVzX2RlY19kZiAlPiUgCiAgc2VsZWN0KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIGBOb3RlIGZpZWxkIGZyb20gTmF1dGlsdXMgb2YgaW5pdGlhbCBwYXJlbnRgKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJuYXV0aWx1c19kZWNfbGlzdC50c3YiKSkKCmBgYAoKIyMgT3RoZXIsIG1heWJlIHRvIGRlbGV0ZQoKYGBgIHtyIG90aGVyLXRoaW5nc30KCiMgTWFrZSBsaXN0IHdpdGggc2FtcGxlcyB3aXRoID4yIGJpb3NwZWNpbWVucyBhdCBEZWNlYXNlZCB0aW1lcG9pbnQKZGVjX25fZGYgPC0gZGYgJT4lIAogIGZpbHRlcih0dW1vcl9kZXNjcmlwdG9yID09ICJEZWNlYXNlZCIpICU+JSAKICBzZWxlY3QoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCwgdHVtb3JfZGVzY3JpcHRvciwgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCkgJT4lIAogIHVuaXF1ZSgpICU+JSAKICBkcGx5cjo6Y291bnQoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lIAogIyBkcGx5cjo6bXV0YXRlKGtpZHNfZGVjX24gPSBnbHVlOjpnbHVlKCJ7S2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRH0gIChOPXtufSkiKSkgJT4lCiAgZHBseXI6OnJlbmFtZShraWRzX2RlY2Vhc2VkX2JzX251bWJlciA9IG4pICU+JSAKICBmaWx0ZXIoIWtpZHNfZGVjZWFzZWRfYnNfbnVtYmVyIDw9IDEpICU+JSAKICB3cml0ZV90c3YoZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAia2lkc19kZWNfbXVsdGlwbGVfYnNfbGlzdC50c3YiKSkKCiMjIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgbGV0J3MgbG9vayBpbnRvIFBUXzNLTTlXOFM4ClBUXzNLTTlXOFM4X2RmIDwtIG5hdXRpbHVzX2RlY19kZiAlPiUgCiAgZmlsdGVyKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQgPT0gIlBUXzNLTTlXOFM4IiwKICAgICAgICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCA9PSAiQlNfMk5RWFk1MjgiKSAlPiUgCiAgc2VsZWN0KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQsIEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQsIGBOb3RlIGZpZWxkIGZyb20gTmF1dGlsdXMgb2YgaW5pdGlhbCBwYXJlbnRgKSAlPiUgCiAgdW5pcXVlKCkgJT4lIAogIHdyaXRlX3RzdihmaWxlLnBhdGgocmVzdWx0c19kaXIsICJuYXV0aWx1c19kZWNfbGlzdC50c3YiKSkKICAKI2JzX2lkIDwtIHByaW50KHVuaXF1ZShQVF8zS005VzhTOF9kZiRLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEKSkKYGBgCgoKIyBDcmVhdGUgcHljbG9uZSBpbnB1dCBmaWxlcyAKCldlIG5lZWQgdG8gZ2VuZXJhdGUgdGhlIGlucHV0IGZpbGVzIGFjY29yZGluZyB0byB0aGUgbWV0aG9kJ3MgdGVtcGxhdGUuIApQaHlsb2dlbmV0aWMgbWV0aG9kcyByZXF1aXJlIGF0IGxlYXN0IDIgc2FtcGxlcyBwZXIgdHVtb3Igc2l0ZSAobXVsdGlyZWdpb25hbCBzYW1wbGluZyBwZXIgYW5hdG9taWNhbCBzaXRlKS4KSGVyZSwgd2Ugd2lsbCBjb25zaWRlciBraWRzIHNhbXBsZXMgd2l0aCBtb3JlIHRoYW4gMiB0aW1lcG9pbnRzIHdpdGggb25lIG9yIG1vcmUgYmlvc3BlY2ltZW5zLiAKV2Ugd2lsbCBjb21wYXJlIGxhdGVyIGRpZmZlcmVuY2VzIGluIHNhbXBsZXMgd2l0aCBzaW5nbGUgdnMgbXVsdGlwbGUgYmlvc3BlY2ltZW5zLgoKYGBge3IgY3JlYXRlLXB5Y2xvbmUtYWxsLXNhbXBsZXMtZGZ9CiMgQ3JlYXRlIHB5Y2xvbmUgZGYgZm9yIGFsbCBzYW1wbGVzCnB5Y2xvbmVfYWxsX3NhbXBsZXNfZGYgPC0gbmF1dGlsdXNfZGVjX2RmICU+JQogIHNlbGVjdChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCBjZ19pZCwgCiAgICAgICAgIGNnX2lkX2tpZHMsIG11dGF0aW9uX2lkLCBzYW1wbGVfaWQsCiAgICAgICAgIHR1bW9yX2Rlc2NyaXB0b3IsIENocm9tb3NvbWUsIFN0YXJ0X1Bvc2l0aW9uLCAKICAgICAgICAgUmVmZXJlbmNlX0FsbGVsZSwgVHVtb3JfU2VxX0FsbGVsZTIsIHRfcmVmX2NvdW50LCB0X2FsdF9jb3VudCwKICAgICAgICAgbm9ybWFsX2NuLCB0dW1vcl9mcmFjdGlvbikgJT4lIAogIAogICMgY2hhbmdlIG5hbWVzIHRvIG1hdGNoIGlucHV0IHJlcXVpcmVtZW50cwogIGRwbHlyOjpyZW5hbWUoInJlZl9jb3VudHMiID0gInRfcmVmX2NvdW50IiwgCiAgICAgICAgICAgICAgICAiYWx0X2NvdW50cyIgPSAidF9hbHRfY291bnQiLAogICAgICAgICAgICAgICAgInR1bW91cl9jb250ZW50IiA9ICJ0dW1vcl9mcmFjdGlvbiIpICU+JQogIHNlbGVjdChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCB0dW1vcl9kZXNjcmlwdG9yLCAKICAgICAgICAgY2dfaWRfa2lkcywgbXV0YXRpb25faWQsIHNhbXBsZV9pZCwgcmVmX2NvdW50cywgCiAgICAgICAgIGFsdF9jb3VudHMsIG5vcm1hbF9jbiwgdHVtb3VyX2NvbnRlbnQpCiAKCiMgSSB3aWxsIHRlc3Qgb25lIEhHRyBkYXRhc2V0IGZvciBub3cKIyBQVF9aNEJGMk5TQgoKI1BUX1o0QkYyTlNCX0RGIDwtIHB5Y2xvbmVfYWxsX3NhbXBsZXNfZGYgJT4lIAojICBmaWx0ZXIoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCA9PSAiUFRfWjRCRjJOU0IiKQogICAgICAgICAgICAgICAgICAgCmBgYAoKIyMgQWRkIG1ham9yX2NuLCBtaW5vcl9jbiBjb2x1bW5zIGZyb20gY25zIGZpbGVzCgpgYGB7ciBwcm9jZXNzLWNucy1maWxlc30KZGF0YV9kaXIgPC0gZGlyKHBhdGggPSBjbnNfZGlyLCAgcGF0dGVybiA9ICIuY2FsbC5jbnMiLCBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkKCiMgQ3JlYXRlIGxpc3QgCmRhdGFfbGlzdCA8LSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChkYXRhX2RpcikgKSB7IAogIAogICMgQ3JlYXRlIHNhbXBsZV9uYW1lCiAgc2FtcGxlX25hbWUgPC0gIHVuaXF1ZShhcy5jaGFyYWN0ZXIoZ3N1YigiLmNhbGwuY25zIiwgIiIsIHN0cl9zcGxpdF9maXhlZChkYXRhX2RpcltpXSwgIi8iLCAxMylbLDExXSkpKQogIHNhbXBsZV9uYW1lIDwtIHNvcnQoc2FtcGxlX25hbWUsIGRlY3JlYXNpbmcgPSBGQUxTRSkKICBwcmludChzYW1wbGVfbmFtZSkKICAKICBmb3IgKHggaW4gc2VxX2Fsb25nKHNhbXBsZV9uYW1lKSApIHsgCiAgICAKICAgIGRhdGFfbGlzdFtbaV1dIDwtIHJlYWQuY3N2KGRhdGFfZGlyW2ldLCBoZWFkZXI9VCwgc2VwPSJcdCIpIAogIAogICAgCiAgIyBDcmVhdGUgZmlsZV9uYW1lCiAgZmlsZV9uYW1lIDwtIGdzdWIoIi5jYWxsLmNucyIsICIiLCBzdHJfc3BsaXRfZml4ZWQoZGF0YV9kaXJbaV0sICIvIiwgMTMpWywxMl0pCiAgcHJpbnQoZmlsZV9uYW1lKQogIAogIGRhdGFfbGlzdFtbaV1dIDwtIGRhdGFfbGlzdFtbaV1dICU+JSAKICAgIG11dGF0ZShLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gZmlsZV9uYW1lKQogIAogICMgVGhlIGZvbGxvd2luZyBjb2RlIGFzc2lnbnMgbmFtZSB0byBkZgogICMgQnV0IGFkZHMgYW4gZXh0cmEgZGYgaW50byB0aGUgbGlzdCAtIGRvbnQga25vdyB5ZXQgd2h5IGJ1dCBsZXQncyByZW1vdmUsIGl0J3Mgbm90IG5lY2Vzc2FyeQogICNkZiA8LSBhc3NpZ24oZmlsZV9uYW1lLCBkYXRhX2xpc3QpIAogICNkYXRhX2xpc3RbW2ZpbGVfbmFtZV1dIDwtIGRmCiAgfQp9CgojIEJpbmQgYWxsIGRmIGZyb20gbGlzdCAKZGF0YV9saXN0X2JpbmQgPC0gZHBseXI6OmJpbmRfcm93cyhkYXRhX2xpc3QpIAoKIyBDcmVhdGUgYW5kIHNhdmUgcHljbG9uZV9pbnB1dCEKcHljbG9uZV9pbnB1dCA8LSBkYXRhX2xpc3RfYmluZCAlPiUgCiAgCiAgIyBSZW5hbWUgdG8gbWF0Y2ggaW5wdXQgZm9ybWF0CiAgIyBUbyBmaWd1cmUgb3V0IGlmIHRoZSBhc3NpZ25tZW50IGlzIGNvcnJlY3QKICBkcGx5cjo6cmVuYW1lKCJtYWpvcl9jbiIgPSAiY24xIiwgCiAgICAgICAgICAgICAgICAibWlub3JfY24iID0gImNuMiIpICU+JSAKCiAgbGVmdF9qb2luKHB5Y2xvbmVfYWxsX3NhbXBsZXNfZGYpICU+JSAKICBmaWx0ZXIoIWlzLm5hKG1ham9yX2NuKSwKICAgICAgICAgIWlzLm5hKG1pbm9yX2NuKSkgJT4lCiAgZHBseXI6Om11dGF0ZShtdXRhdGlvbl9pZCA9IHBhc3RlKG11dGF0aW9uX2lkLCBtYWpvcl9jbiwgbWlub3JfY24sc2VwID0gIjoiKSkgJT4lIAogIHNlbGVjdChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lELCB0dW1vcl9kZXNjcmlwdG9yLCBjZ19pZF9raWRzLCBtdXRhdGlvbl9pZCwgc2FtcGxlX2lkLCByZWZfY291bnRzLCAKICAgICAgICAgYWx0X2NvdW50cywgbm9ybWFsX2NuLCBtYWpvcl9jbiwgbWlub3JfY24sIHR1bW91cl9jb250ZW50KSAlPiUgCiAgCiAgIyBUbyBlbnN1cmUgdGhlcmUgYXJlIG5vIGR1cGxpY2F0ZWQgZW50cmllcyBpbiB0aGUgZGF0YWZyYW1lCiAgZGlzdGluY3QoKSAKCgpmb3IgKGkgaW4gMTpsZW5ndGgoZGF0YV9kaXIpICkgeyAKICAKICAjIENyZWF0ZSBzYW1wbGVfbmFtZQogIHNhbXBsZV9uYW1lIDwtICB1bmlxdWUoYXMuY2hhcmFjdGVyKGdzdWIoIi5jYWxsLmNucyIsICIiLCBzdHJfc3BsaXRfZml4ZWQoZGF0YV9kaXJbaV0sICIvIiwgMTMpWywxMV0pKSkKICBzYW1wbGVfbmFtZSA8LSBzb3J0KHNhbXBsZV9uYW1lLCBkZWNyZWFzaW5nID0gRkFMU0UpCiAgcHJpbnQoc2FtcGxlX25hbWUpCiAgCmZvciAoeCBpbiBzZXFfYWxvbmcoc2FtcGxlX25hbWUpICkgeyAKICAKICAjIFNhdmUgaW5wdXQgZmlsZSBmb3IgcHljbG9uZS12aQogIGZuYW1lIDwtIHBhc3RlMChweWNsb25ldmlfaW5wdXRfZGlyLCAiLyIsIHNhbXBsZV9uYW1lW3hdLCAiLnRzdiIpCiAgcHJpbnQoZm5hbWUpCiAgCiAgcHljbG9uZV9pbnB1dF9zdWJzZXQgPC0gcHljbG9uZV9pbnB1dCAlPiUKICAgIGZpbHRlcihLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEID09IHNhbXBsZV9uYW1lW3hdKSAlPiUgCiAgICAjc2VsZWN0KC1jKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpKSAlPiUgCiAgd3JpdGVfdHN2KGZpbGUucGF0aChmbmFtZSkpCiAgCiAgCiAgIyBTYXZlIGlucHV0IGZpbGUgZm9yIEZhc3RDbG9uZQogIGZhc3RjbG9uZV9mbmFtZSA8LSBwYXN0ZTAoZmFzdGNsb25lX2lucHV0X2RpciwgIi8iLCBzYW1wbGVfbmFtZVt4XSwgIi50c3YiKQogIHByaW50KGZhc3RjbG9uZV9mbmFtZSkKICAKICBmYXN0Y2xvbmVfaW5wdXRfc3Vic2V0IDwtIHB5Y2xvbmVfaW5wdXQgJT4lCiAgICBkcGx5cjo6cmVuYW1lKCJ2YXJfY291bnRzIiA9ICJhbHRfY291bnRzIikgJT4lIAogICAgZmlsdGVyKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQgPT0gc2FtcGxlX25hbWVbeF0pICU+JSAKICAgICNzZWxlY3QoLWMoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkpICU+JSAKICB3cml0ZV90c3YoZmlsZS5wYXRoKGZhc3RjbG9uZV9mbmFtZSkpCiAgICAKICAgIAogICAgCn0KfQoKCiMgVG8gZmluZCB0aGUgcG9zaXRpb24gb2YgZHVwbGljYXRlIGVsZW1lbnRzIGluIHgsIHVzZSB0aGlzOgojIGR1cGxpY2F0ZWQocHljbG9uZV9pbnB1dCkKCiMgRXh0cmFjdCBkdXBsaWNhdGUgZWxlbWVudHM6CiMgcHljbG9uZV9pbnB1dFtkdXBsaWNhdGVkKHB5Y2xvbmVfaW5wdXQpXQoKYGBgCgojIFBsb3QgZGVwdGggY292ZXJhZ2UKCmBgYHtyIGRlZmluZS1wYXJhbWV0ZXJzLWZvci1wbG90c30KIyBSZWFkIGNvbG9yIHBhbGV0dGUKcGFsZXR0ZV9kZiA8LSByZWFkcjo6cmVhZF90c3YocGFsZXR0ZV9maWxlLCBndWVzc19tYXggPSAxMDAwMDAsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpICU+JSAKICBtdXRhdGUodHVtb3JfZGVzY3JpcHRvciA9IGNvbG9yX25hbWVzKQoKIyBEZWZpbmUgYW5kIG9yZGVyIHBhbGV0dGUKcGFsZXR0ZSA8LSBwYWxldHRlX2RmJGhleF9jb2RlcwpuYW1lcyhwYWxldHRlKSA8LSBwYWxldHRlX2RmJHR1bW9yX2Rlc2NyaXB0b3IKCiMgRGVmaW5lIHRpbWVwb2ludHMKdGltZXBvaW50cyA9IGMoIkRpYWdub3NpcyIsICJQcm9ncmVzc2l2ZSIsICJSZWN1cnJlbmNlIiwgIkRlY2Vhc2VkIiwgIlNlY29uZCBNYWxpZ25hbmN5IiwgIlVuYXZhaWxhYmxlIikKCmBgYAoKYGBgIHtyIGRlcHRoLWNvdmVyYWdlfQpmb3IgKGkgaW4gMTpsZW5ndGgoZGF0YV9kaXIpICkgeyAKICAKICAjIENyZWF0ZSBzYW1wbGVfbmFtZQogIHNhbXBsZV9uYW1lIDwtICB1bmlxdWUoYXMuY2hhcmFjdGVyKGdzdWIoIi5jYWxsLmNucyIsICIiLCBzdHJfc3BsaXRfZml4ZWQoZGF0YV9kaXJbaV0sICIvIiwgMTMpWywxMV0pKSkKICBzYW1wbGVfbmFtZSA8LSBzb3J0KHNhbXBsZV9uYW1lLCBkZWNyZWFzaW5nID0gRkFMU0UpCiAgcHJpbnQoc2FtcGxlX25hbWUpCiAgCmZvciAoeCBpbiBzZXFfYWxvbmcoc2FtcGxlX25hbWUpICkgeyAKICAKICBweWNsb25lX2lucHV0X3N1YnNldCA8LSBweWNsb25lX2lucHV0ICU+JQogICAgZmlsdGVyKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQgPT0gc2FtcGxlX25hbWVbeF0pICU+JSAKICAgIHNlbGVjdCgtYyhLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSkgIyU+JSAKICAgIyBtdXRhdGUodHVtb3JfZGVzY3JpcHRvciA9IGZhY3Rvcih0dW1vcl9kZXNjcmlwdG9yKSwKICAgICMgICAgIHR1bW9yX2Rlc2NyaXB0b3IgPSBmY3RfcmVsZXZlbCh0dW1vcl9kZXNjcmlwdG9yLCB0aW1lcG9pbnRzKSkgJT4lIAogICAgI2FycmFuZ2UodHVtb3JfZGVzY3JpcHRvciwgc2FtcGxlX2lkKQogIAogICMgTWFrZSB0aGlzIHJlcHJvZHVjaWJsZQogIHNldC5zZWVkKDIwMjMpCgogICMgRGVmaW5lIGxhYmVsIGZvciBwbG90cwogIFRpbWVwb2ludCA8LSBmYWN0b3IoeCA9IHB5Y2xvbmVfaW5wdXRfc3Vic2V0JHR1bW9yX2Rlc2NyaXB0b3IsIGxldmVscyA9IHRpbWVwb2ludHMpCiAgCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKICAjIENyZWF0ZSBieHAgcmVmX2NvdW50cwogICAgcCA8LSBwcmludChnZ3Bsb3QocHljbG9uZV9pbnB1dF9zdWJzZXQsIGFlcyhzYW1wbGVfaWQsIHJlZl9jb3VudHMsIGNvbG9yID0gVGltZXBvaW50KSkgKyAKICAgICAgICAgICAgICAgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMTUsIHNpemUgPSAwLjcsIGFscGhhID0gMC42KSArCiAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6Z2VvbV9ib3hwbG90KGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29lZiA9IDApICsgIyByZW1vdmUgd2hpc2tlcnMKICAgICAgICAgICAgICAgICB0aGVtZV9QdWJsaWNhdGlvbigpICsgCiAgICAgICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNvcnQobmFtZXMocGFsZXR0ZSkpKSArCiAgICAgICAgICAgICAgICAgI3JvdGF0ZSgpICsKICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKwogICAgICAgICAgICAgICAgIHN0YXRfc3VtbWFyeShmdW4ueT1tZWFuLHNoYXBlPTEsY29sPSdibGFjaycsZ2VvbT0ncG9pbnQnKSArCiAgICAgICAgICAgICAgICAgbGFicyh0aXRsZSA9IHNhbXBsZV9uYW1lW3hdLAogICAgICAgICAgICAgICAgICAgICAgeCA9ICJzYW1wbGVfaWQiLAogICAgICAgICAgICAgICAgICAgICAgeSA9ICJyZWZfY291bnRzIiwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gIlRpbWVwb2ludCIpKQogICAgCiAgICAjIFNhdmUgdGhlIHBsb3QKICAgIGdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChzYW1wbGVfbmFtZVt4XSwgIi1yZWZfY291bnRzLnBkZiIpLCAKICAgICAgICAgcGF0aCA9IHB5Y2xvbmVfcGxvdHNfZGlyLCAKICAgICAgICAgd2lkdGggPSA2LCAKICAgICAgICAgaGVpZ2h0ID0gNSwgCiAgICAgICAgIGRldmljZSA9ICJwZGYiLCAKICAgICAgICB1c2VEaW5nYmF0cyA9IEZBTFNFKQogICAgCgogICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiAgIyBDcmVhdGUgYnhwIGFsdF9jb3VudHMKICAgIHAgPC0gcHJpbnQoZ2dwbG90KHB5Y2xvbmVfaW5wdXRfc3Vic2V0LCBhZXMoc2FtcGxlX2lkLCBhbHRfY291bnRzLCBjb2xvciA9IFRpbWVwb2ludCkpICsgCiAgICAgICAgICAgICAgICAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjE1LCBzaXplID0gMC43LCBhbHBoYSA9IDAuNikgKwogICAgICAgICAgICAgICAgIGdncGxvdDI6Omdlb21fYm94cGxvdChjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAwLjI1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvZWYgPSAwKSArICMgcmVtb3ZlIHdoaXNrZXJzCiAgICAgICAgICAgICAgICAgdGhlbWVfUHVibGljYXRpb24oKSArIAogICAgICAgICAgICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzb3J0KG5hbWVzKHBhbGV0dGUpKSkgKwogICAgICAgICAgICAgICAgICNyb3RhdGUoKSArCiAgICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpICsKICAgICAgICAgICAgICAgICBzdGF0X3N1bW1hcnkoZnVuLnk9bWVhbixzaGFwZT0xLGNvbD0nYmxhY2snLGdlb209J3BvaW50JykgKwogICAgICAgICAgICAgICAgIGxhYnModGl0bGUgPSBzYW1wbGVfbmFtZVt4XSwKICAgICAgICAgICAgICAgICAgICAgIHggPSAic2FtcGxlX2lkIiwKICAgICAgICAgICAgICAgICAgICAgIHkgPSAiYWx0X2NvdW50cyIsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJUaW1lcG9pbnQiKSkKICAgIAogICAgIyBTYXZlIHRoZSBwbG90CiAgICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoc2FtcGxlX25hbWVbeF0sICItYWx0X2NvdW50cy5wZGYiKSwgCiAgICAgICAgIHBhdGggPSBweWNsb25lX3Bsb3RzX2RpciwgCiAgICAgICAgIHdpZHRoID0gNiwgCiAgICAgICAgIGhlaWdodCA9IDUsIAogICAgICAgICBkZXZpY2UgPSAicGRmIiwgCiAgICAgICAgdXNlRGluZ2JhdHMgPSBGQUxTRSkKICAKICAKfQp9CgpgYGAKCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCgo=